﻿// ———————————————————————–
// <copyright file="DEPostService.svc.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// DEPostService.svc.cs - "Parses DEs And Puts Them In Appropriate ValueLists"
// Project: "ICNETSOA"- "ICNETREST"
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2010
// Author:      Don McGarry The MITRE Corporation
/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-09-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)
                
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Xml.Linq;
using EDXLSharp;
using EDXLSharp.EDXLDELib;
using ICNETLogging;
using ICNETServices;
using System;
using System.Net;
using System.IO;
using System.Collections.Specialized;
using System.Web;
using System.ServiceModel.Channels;
using EDXLHistory;

namespace ICNETREST
{

  /// <summary>
  /// Methods to parse a DE and send it to be processed.
  /// </summary>
  [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
  [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
  public class DEPostService : IDEPost
  {

    /// <summary>
    /// Handles a post of plain text instead of xml, to allow cross domain javascript to work.
    /// Then Figures out the type of content object is in the DE, and puts it in the appropriate database. 
    /// </summary>
    /// <param name="input">The DE Object thta will be parsed</param>
    /// <returns>The DE Object as it was passed in.</returns>
    [WebInvoke(UriTemplate = "crossdomain")]
    public XElement CrossDomainCreate(Stream input)
      {
        //WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
        string senderIP = (OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty).Address;
        string senderURI = OperationContext.Current.IncomingMessageHeaders.To.OriginalString;

        StreamReader sr = new StreamReader(input); 
        string body = sr.ReadToEnd(); sr.Dispose();
        
        XElement xml = XElement.Parse(body);

        if (xml == null)
        {
            WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
            WebOperationContext.Current.OutgoingResponse.StatusDescription = "Post Failed. Provided XML is missing.";
            return new XElement("Failure");
        }
        else if (!xml.HasElements)
        {
            WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
            WebOperationContext.Current.OutgoingResponse.StatusDescription = "Post Failed. Provided XML is incomplete.";
            return new XElement("Failure");
        }

        XElement results = AddToDatabase(senderIP, senderURI, xml);
        return results;
    }


    /// <summary>
    /// Test the cross domain posting gateway
    /// </summary>
    /// <param name="input">The DE Object thta will be parsed</param>
    /// <returns>The DE Object as it was passed in.</returns>
    [WebInvoke(UriTemplate = "crossdomaintest")]
    public XElement CrossDomainTest(Stream input)
    {
        StreamReader sr = new StreamReader(input);
        string body = sr.ReadToEnd(); sr.Dispose();

        XElement xml = XElement.Parse(body);

        return xml;
    }

    /// <summary>
    /// Test the cross domain posting gateway, with a manually built header
    /// </summary>
    /// <param name="input">The DE Object thta will be parsed</param>
    /// <returns>The DE Object as it was passed in.</returns>
    [WebInvoke(UriTemplate = "crossdomaintest2")]
    public XElement CrossDomainTest2(Stream input)
    {
        WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*");
        StreamReader sr = new StreamReader(input);
        string body = sr.ReadToEnd(); sr.Dispose();

        XElement xml = XElement.Parse(body);

        return xml;
    }


    /// <summary>
    /// Figures out the type of content object is in the DE, and puts it in the appropriate database. 
    /// </summary>
    /// <param name="xe">The DE Object thta will be parsed</param>
    /// <returns>The DE Object as it was passed in.</returns>
    [WebInvoke(UriTemplate = "", Method = "POST", RequestFormat = WebMessageFormat.Xml, ResponseFormat = WebMessageFormat.Xml, BodyStyle = WebMessageBodyStyle.Bare)]
    public XElement Create(XElement xe)
    {
       string senderIP = (OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as RemoteEndpointMessageProperty).Address;
       string senderURI = OperationContext.Current.IncomingMessageHeaders.To.OriginalString;

      if (xe == null)
      {
        WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
        WebOperationContext.Current.OutgoingResponse.StatusDescription = "Post Failed. Provided XML is missing.";
        return new XElement("Failure");
      }
      else if (!xe.HasElements)
      {
        WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.BadRequest;
        WebOperationContext.Current.OutgoingResponse.StatusDescription = "Post Failed. Provided XML is incomplete.";
        return new XElement("Failure");
      }
      else
      {
        try
        {
            return AddToDatabase(senderIP, senderURI, xe);
        }
        catch(Exception ex)
        {
          WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.InternalServerError;
          WebOperationContext.Current.OutgoingResponse.StatusDescription = "Post Failed. Internal Error:"+ex.Message;
          return new XElement("Failure", ex.Message);
        }
      }
    }
    [WebInvoke(UriTemplate="", Method="OPTIONS")]
    public string Cors()
    {
      return "great success";
    }


    private XElement AddToDatabase(string senderIP, string senderURI, XElement xe)
    {
        int loggingID = Logging.AddLoggingEventToDB(senderIP, senderURI, xe);
        Logging.AddEventServiceToDB(loggingID, "ICNETREST-DEPostService");
        string s = xe.ToString(SaveOptions.None);
        EDXLDE distributionElement = new EDXLDE(s);

        if (ICNETSOAConstants.HistoryDatabaseEnabled)
        {
          int historyId = History.AddReceivedMessageToDB(distributionElement.DistributionID, distributionElement.SenderID, distributionElement.DateTimeSent.ToLocalTime(), senderIP, senderURI, xe);
        }

        // remove SenderRoles that are marked as 'internal', they are potentially harmful to the normal message flow
        // (two step pruning to keep inerator legal)
        // TODO: Check the 'value' instead of the 'urn' for bad sender roles
        List<ValueList> invalidSenderRoles = new List<ValueList>();
        //// the host name is not important
        foreach (ValueList list in distributionElement.SenderRole)
        {
            if (list.ValueListURN.Contains("SenderRole/InternalServices"))
            {
                invalidSenderRoles.Add(list);
            }
        }

        foreach (ValueList list in invalidSenderRoles)
        {
            distributionElement.SenderRole.Remove(list);
        }

        // New feature- if special flag is set, test the DeadLetter service by intentionally not filling in a usable SenderRole
        // Added by rgustafson while tracking down http://jira.mitre.org/browse/ICNET-212
        bool testingDeadLetter = false;
        foreach (ValueList list in distributionElement.SenderRole)
        {
            if (list.ValueListURN.Contains("SenderRole/DeadLetterTest"))
            {
                testingDeadLetter = true;
            }
        }

        if (!testingDeadLetter)
        {
            ValueList servicerole = new ValueList();
            servicerole.ValueListURN = ICNETSOAConstants.ICNETServicesURI;
            servicerole.Value.Add(ICNETSOAConstants.DEPostServiceNamespace);
            distributionElement.SenderRole.Add(servicerole);
        }
        
        RouterClient client = new RouterClient();
        client.ProcessDE(distributionElement);

        if (ICNETSOAConstants.HistoryDatabaseEnabled)
        {
          Logging.ChangeEventServiceStatus(loggingID, "ICNETREST-DEPostService", Logging.StatusCodes.Done_OK);
          History.AddServiceStatusToDB(distributionElement.DistributionID, distributionElement.SenderID, distributionElement.DateTimeSent.ToLocalTime(),
            "DEPostService", History.ServiceStatusEnum.Success, DateTime.Now, "");
        }
        return xe;
    }
  
  }
}
